﻿using System;
using System.Collections;
using System.Xml;
using MediaBot.Entities;
using MediaBot.Library;
using RakutenCrawler.ConfigClass;
using RakutenCrawler.Repository;
using System.Linq;

namespace RakutenCrawler.PageProcessors
{
    public class ProductsPageProcessor : PageProcessor
    {
        const int MaxProductsToRetrieve = 150;
        public override bool Execute()
        {
            log.Info("ProductsPageProcessor.Execute()");
            log.Info("Link: " + Link.Url);

            var xmlSerializer = new XmlSerializer<ProductsConfig>();
            var config = xmlSerializer.Deserialize(LinkConfig.BuildPath());

            var ns = new NamespaceUriItem { Name = "ns", Value = "http://www.w3.org/1999/xhtml" };
            var namespaces = new ArrayList { ns };

            var linkRepo = new LinkRepository();
            var productRepo = new ProductRepository();
            var manuRepo = new ManufacturerRepository();

            try
            {
                var url = Link.Url;

                var pageNumber = 0;

                while (url != null)
                {
                    var count = productRepo.FindBy(p => p.ProductCategoryId == Link.ProductCategoryId).ToList().Count;
                    if (count >= MaxProductsToRetrieve)
                    {
                        Console.WriteLine("Enough products, getting out");
                        return true;
                    }


                    pageNumber++;
                    log.Info("Page " + pageNumber);

                    var xmlWebPage = new XmlWebPage(new Uri(url), namespaces);
                    xmlWebPage.Download();

                    var nextPageUrl = xmlWebPage.GetString(config.Products.NextPage);
                    url = string.IsNullOrEmpty(nextPageUrl) ? null : nextPageUrl;

                    if (pageNumber < Link.Page)
                        continue;

                    Link.Page = pageNumber;
                    linkRepo.InsertOrUpdate(Link);
                    linkRepo.Save();

                    var nsManager = xmlWebPage.XmlNamespaceManager;

                    var productNodes = xmlWebPage.SelectNodes(config.Products.ProductNode.Context);

                    foreach (XmlNode productNode in productNodes)
                    {
                        try
                        {
                            var productName = config.Products.ProductName.GetString(productNode, nsManager);
                            var productUrl = config.Products.ProductUrl.GetString(productNode, nsManager);

                            Console.WriteLine("LinkId: {0}, Page: {1}, ProductName: {2}", Link.LinkId, pageNumber, productName);

                            string price = string.Empty;
                            string thumbnail = string.Empty;
                            double? rating = 0.0;
                            DateTime? releasedDate = null;
                            string shortDescription = string.Empty;

                            Manufacturer manufacturer = null;

                            if (config.Products.ManufacturerName != null)
                            {
                                var manufacuturerName = config.Products.ManufacturerName.GetString(productNode, nsManager);
                                manufacturer = new Manufacturer { ManufacturerName = manufacuturerName };
                                if (config.Products.ManufacturerUrl != null)
                                {
                                    var mUrl = config.Products.ManufacturerUrl.GetString(productNode, nsManager);
                                    manufacturer.ManufacturerURL = mUrl;
                                }

                                manuRepo.InsertOrUpdate(manufacturer);
                                manuRepo.Save();
                            }

                            if (config.Products.Price != null)
                            {
                                price = config.Products.Price.GetString(productNode, nsManager);
                            }

                            if (config.Products.ThumbnailImage != null)
                            {
                                thumbnail = config.Products.ThumbnailImage.GetString(productNode, nsManager);
                            }

                            if (config.Products.AverageRating != null)
                            {
                                var ratingStr = config.Products.AverageRating.GetString(productNode, nsManager);
                                if (!string.IsNullOrEmpty(ratingStr))
                                    rating = double.Parse(ratingStr);
                                else
                                {
                                    log.Warn(string.Format("rating string empty - page: {0} - productName: {1}", pageNumber, productName));
                                    rating = 0.0;
                                }
                            }

                            if (config.Products.ReleasedDate != null)
                            {
                                var dateStr = config.Products.ReleasedDate.GetString(productNode, nsManager);
                                if (!string.IsNullOrEmpty(dateStr))
                                    releasedDate = DateTime.Parse(dateStr);
                                else
                                {
                                    log.Warn(string.Format("releasedate string empty - page: {0} - productName: {1}", pageNumber, productName));
                                }
                            }

                            if (config.Products.ShortDescription != null)
                            {
                                shortDescription = config.Products.ShortDescription.GetString(productNode, nsManager);
                            }

                            var product = new Product
                            {
                                ProductName = productName,
                                ProductURL = productUrl,
                                Price = price,
                                Thumbnail = thumbnail,
                                AverageRating = rating,
                                ReleasedDate = releasedDate,
                                ShortDescription = shortDescription,
                            };

                            if (config.Products.ShopName != null)
                            {
                                product.ShopName = config.Products.ShopName.GetString(productNode, nsManager);
                            }

                            if (config.Products.ShopUrl != null)
                            {
                                product.ShopUrl = config.Products.ShopUrl.GetString(productNode, nsManager);
                            }

                            if (manufacturer != null)
                                product.ManufacturerId = manufacturer.ManufacturerId;

                            if (Link.ProductCategoryId != null)
                                product.ProductCategoryId = Link.ProductCategoryId;

                            productRepo.InsertOrUpdate(product);
                            productRepo.Save();

                            GetProductDetail(product, config.ProductDetail, productRepo, manuRepo, namespaces);
                        }
                        catch (Exception e)
                        {
                            log.Error("ProductsPageProcessor - Getting products exception", e);
                        }
                    }

                }
            }
            catch (Exception e)
            {
                log.Error("ProductsPageProcessor.Execute() exception", e);
                return false;
            }
            finally
            {
                linkRepo.Dispose();
                productRepo.Dispose();
                manuRepo.Dispose();
            }

            return true;
        }


        private void GetProductDetail(Product product, ProductDetail detailConfig, ProductRepository productRepo, ManufacturerRepository manuRepo, ArrayList namespaces)
        {
            if (detailConfig == null)
                return;

            log.Info("Getting detail of product: " + product.ProductName);
            log.Info("Product detail Url: " + product.ProductURL);

            try
            {
                var xmlWebPage = new XmlWebPage(new Uri(product.ProductURL), namespaces);
                xmlWebPage.Download();

                var nsManager = xmlWebPage.XmlNamespaceManager;

                var detailNodes = xmlWebPage.SelectNodes(detailConfig.ProductDetailNode.Context);
                if (detailNodes.Count == 0)
                    return;

                var detailNode = detailNodes[0];
                if (string.IsNullOrEmpty(product.Price) && detailConfig.Price != null)
                {
                    product.Price = detailConfig.Price.GetString(detailNode, nsManager);
                }

                if (!product.AverageRating.HasValue && detailConfig.AverageRating != null)
                {
                    var ratingStr = detailConfig.AverageRating.GetString(detailNode, nsManager);
                    if (!string.IsNullOrEmpty(ratingStr))
                        product.AverageRating = double.Parse(ratingStr);
                    else
                    {
                        log.Warn(string.Format("rating string empty - ProductUrl: {0}", product.ProductURL));
                        product.AverageRating = 0.0;
                    }
                }

                if (product.ReleasedDate == null && detailConfig.ReleasedDate != null)
                {
                    var dateStr = detailConfig.ReleasedDate.GetString(detailNode, nsManager);

                    if (!string.IsNullOrEmpty(dateStr))
                        product.ReleasedDate = DateTime.Parse(dateStr);
                    else
                    {
                        log.Warn(string.Format("releasedate string empty - ProductUrl: {0}", product.ProductURL));
                    }
                }

                if (product.ManufacturerId == null && detailConfig.ManufacturerName != null)
                {
                    var manufacturerName = detailConfig.ManufacturerName.GetString(detailNode, nsManager);
                    var manufacturer = new Manufacturer { ManufacturerName = manufacturerName };
                    if (detailConfig.ManufacturerUrl != null)
                        manufacturer.ManufacturerURL = detailConfig.ManufacturerUrl.GetString(detailNode, nsManager);

                    manuRepo.InsertOrUpdate(manufacturer);
                    manuRepo.Save();

                    product.ManufacturerId = manufacturer.ManufacturerId;
                }

                if (detailConfig.DetailDescription != null)
                    product.DetailDescription = detailConfig.DetailDescription.GetString(detailNode, nsManager);

                if (string.IsNullOrEmpty(product.ShopName) && detailConfig.ShopName != null)
                {
                    product.ShopName = detailConfig.ShopName.GetString(detailNode, nsManager);
                }

                if (string.IsNullOrEmpty(product.ShopUrl) && detailConfig.ShopUrl != null)
                {
                    product.ShopUrl = detailConfig.ShopUrl.GetString(detailNode, nsManager);
                }

                product.DetailHtml = detailNode.InnerXml;

                productRepo.InsertOrUpdate(product);
                productRepo.Save();
            }
            catch (Exception e)
            {
                log.Error("GetProductDetail exception", e);
            }

            log.Info("Finished getting product detail");
        }
    }
}
